package com.bioscript.standalone.imps;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.bioscript.standalone.IO.FileReader;
import com.bioscript.standalone.IO.FileWriter;
import com.bioscript.standalone.dataStructures.Contig;
import com.bioscript.standalone.dataStructures.Range;

public class ContigsProcessor {

	private List<Contig> contigs = null;
	private List<Range> gaps = null;
	private int referenceSize = 0;
	private int overLap = 0;
	private int index = 0;
	
	
	List<Contig> getContigs() {
		return contigs;
	}

	List<Range> getGaps() {
		return gaps;
	}

	public static void main(String[] args)
	{
		ContigsProcessor contigsProcessor = new ContigsProcessor();
		contigsProcessor.referenceSize = Integer.parseInt(args[0]);
		if (!contigsProcessor.createList(args[1])) return;
		FileWriter.writeToFile(args[2],Contig.getList(contigsProcessor.getContigs()));
		FileWriter.writeToFile(args[3],Range.getStringList(contigsProcessor.getGaps()));
		if (!contigsProcessor.createSummary(args[4])) return;
		if (args.length == 6)
		{
			contigsProcessor.createPseudoMolecule(args[1], args[5], "pseudo");
		}
	}
	
	public boolean createSummary(String fileName)
	{
		int totalLength = 0;
		int gcContent = 0;
		int reads = 0;
		float coverage = 0;
		for (Contig contig : contigs)
		{
			totalLength = totalLength + contig.getContig().getLength();
			gcContent = gcContent + contig.getCgs();
			reads = reads + contig.getReads();
			coverage = coverage + contig.getCoverage();
		}
		
		try
		{
			PrintWriter pw = new PrintWriter(fileName);
			pw.println("Total Length\t" + totalLength);
			pw.println("GC Content\t" + gcContent);
			pw.println("Reads\t" + reads);
			pw.println("Coverage\t" + coverage);
			pw.println("Gaps size\t" + (referenceSize - totalLength));
			pw.println("Number of Contigs\t" + contigs.size());
			pw.println("Number of Gaps\t" + gaps.size());
			pw.println("Number of Overlaps\t" + overLap);
			pw.flush();
			pw.close();
			return true;
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		return false;
	}
	
	public boolean createList(String contigsFile)
	{
		List<String> headers = FileReader.readFastaHeaders(contigsFile);
		contigs = new ArrayList<Contig>();
		gaps = new ArrayList<Range>();
		
		Contig previousContig = null;
		for(String header : headers)
		{
			Contig contig = Contig.getInstance(header.trim());
			if (previousContig == null)
			{
				// add the starting gap
				if (contig.getMapping().getLowerLimit() > 1)
				{
					Range gap = new Range(1, contig.getMapping().getLowerLimit() - 1, 
							"initial");
					gaps.add(gap);
				}
			}
			else
			{
				Range mapping = previousContig.getMapping();
				if (mapping.getUpperLimit() < contig.getMapping().getLowerLimit() && 
						(contig.getMapping().getLowerLimit() - mapping.getUpperLimit() > 1))
				{
					Range gap = new Range(mapping.getUpperLimit() + 1, contig.getMapping().getLowerLimit() - 1,
							previousContig.getContig().getName());
					gaps.add(gap);
				}
				else
				{
					overLap++;
				}
			}
			previousContig = contig;
			contigs.add(contig);
		}
		
		// add the last gap
		if (previousContig != null && 
				(referenceSize - previousContig.getMapping().getUpperLimit()) > 1)
		{
			int lowerLimit = previousContig.getMapping().getUpperLimit() + 1;
			int upperLimit = contigs.get(0).getMapping().getLowerLimit() > 0 ? referenceSize : 
				referenceSize + contigs.get(0).getMapping().getLowerLimit() - 1;
			if (upperLimit >= lowerLimit)
			{
				Range gap = new Range(lowerLimit, upperLimit,previousContig.getContig().getName());
				gaps.add(gap);
			}
		}
		return true;
	}
	
	/**
	 * This method should be called only for mapped contigs
	 * not for Denovo contigs. For Denovo contigs use the 
	 * pseudomolecule generated by show-tiling
	 * @param contigsFile
	 * @param pseudoMoleculeFile
	 * @param headerName
	 * @return
	 */
	public boolean createPseudoMolecule(String contigsFile, String pseudoMoleculeFile, String headerName)
	{
		try
		{
			PrintWriter pw = new PrintWriter(pseudoMoleculeFile);
			pw.println(">" + headerName);
			FileReader fileReader = new FileReader();
			Map<String,List<String>> map = fileReader.contigsFileReader(contigsFile);
			map =reCreateMap(map);
			StringBuffer strBuff = new StringBuffer();
			int index = 0;
			for (Contig contig : contigs)
			{
				String name = contig.getContig().getName();
				Range mappedRange = contig.getMapping();
				if (mappedRange.getLowerLimit() > index + 1)
				{
					int length = mappedRange.getLowerLimit() - index - 1;
					strBuff.append(getNs(length));
				}
				List<String> lst = map.get(">" + name);
				for (String str : lst)
				{
					strBuff.append(str);
					if (strBuff.length() > 500)
					{
						index = index + strBuff.length();
						clearBuffer(strBuff, pw);
						strBuff = new StringBuffer();
					}
				}
				if (strBuff.length() > 0)
				{
					index = index + strBuff.length();
					clearBuffer(strBuff, pw);
					strBuff = new StringBuffer();
				}
			}
			if (strBuff.length() > 0)
			{
				clearBuffer(strBuff, pw);
			}
			pw.flush();
			pw.close();
			return true;
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		return false;
	}

	private Map<String, List<String>> reCreateMap(Map<String, List<String>> map) {
		
		Map<String, List<String>> map2 = new HashMap<String, List<String>>();
		Set<String> keySet = map.keySet();
		for(String str : keySet)
		{
			map2.put(str.split(" ")[0], map.get(str));
		}
		return map2;
	}

	private void clearBuffer(StringBuffer strBuff, PrintWriter pw) {
		String temp = strBuff.toString();
		for(int i = 0 ; i < temp.length() ; i++)
		{
			pw.print(temp.charAt(i));
			index++;
			if ( index%60 == 0)
			{
				pw.print("\n");
			}
		}
	}

	private String getNs(int length) {
		StringBuffer buffer = new StringBuffer();
		for (int i = 0 ; i < length ; i++)
		{
			buffer.append("N");
		}
		return buffer.toString();
	}
}
